package net.oschina.archx.spring.mongo.repository;

import com.mongodb.WriteResult;
import net.oschina.archx.spring.mongo.model.Pagination;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import java.io.Serializable;
import java.util.List;

/**
 * MongoRepository MongoDB数据持久化抽象
 *
 * @author Archx[archx@foxmail.com]
 * @date 2016/3/22 0022
 */
public abstract class MongoRepository<M extends Serializable> {

    private static final int MAX_PAGES = 5000;

    /**
     * 日志
     */
    protected final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    protected MongoTemplate mongoTemplate;

    /**
     * 返回实体类型
     *
     * @return class
     */
    public abstract Class<M> getEntityClass();

    /**
     * 通过条件查询实体(集合)
     *
     * @param query 条件
     */
    public List<M> find(Query query) {
        return mongoTemplate.find(query, getEntityClass());
    }

    /**
     * 分页查询
     *
     * @param query 条件
     * @param page  分页模型
     * @return list
     */
    public List<M> findAll(Query query, Pagination page) {
        if (page.getTotal() == null)
            pageCount(query, page);
        query.skip(page.getOffset()).limit(page.getLimit());
        return mongoTemplate.find(query, getEntityClass());
    }

    /**
     * 通过一定的条件查询一个实体
     *
     * @param query 条件
     * @return m
     */
    public M findOne(Query query) {
        return mongoTemplate.findOne(query, getEntityClass());
    }

    /**
     * 通过条件查询更新数据
     *
     * @param query  条件
     * @param update 更新字段
     */
    public M update(Query query, Update update) {
        return mongoTemplate.findAndModify(query, update, getEntityClass());
    }

    /**
     * 保存一个对象到mongodb
     *
     * @param entity 实体
     * @return m
     */
    public M save(M entity) {
        mongoTemplate.save(entity);
        return entity;
    }

    /**
     * 通过ID获取记录
     *
     * @param id 标识
     * @return m
     */
    public M findById(Serializable id) {
        return mongoTemplate.findById(id, this.getEntityClass());
    }

    /**
     * 通过ID获取记录,并且指定了集合名(表的意思)
     *
     * @param id             标识
     * @param collectionName 集合名
     * @return m
     */
    public M findById(Serializable id, String collectionName) {
        return mongoTemplate.findById(id, this.getEntityClass(), collectionName);
    }

    /**
     * 删除一个实体
     *
     * @param entity 实体
     */
    public int delete(M entity) {
        WriteResult remove = mongoTemplate.remove(entity);
        return remove.getN();
    }

    /**
     * 分页统计
     *
     * @param query 条件
     * @return count
     */
    public long pageCount(Query query, Pagination pagination) {
        int offset = (MAX_PAGES - 1) * pagination.getLimit();
        long total = 0l;
        // 跳过五千页查询
        query.skip(offset).limit(pagination.getLimit());
        List<M> list = mongoTemplate.find(query, getEntityClass());
        // 如果五千页后存在数据， 则返回 PAGE_SIZE * (MAX_PAGE - 1)
        if (list != null && list.size() > 0) {
            logger.info("记录数据超过{}页，性能考虑返回记录条数{}", MAX_PAGES, total);
            total = MAX_PAGES * pagination.getLimit() * 1L;
        } else {
            total = mongoTemplate.count(query, getEntityClass());
        }
        pagination.setTotal(total);
        return total;
    }

    /**
     * 分页统计
     *
     * @param query 条件
     * @return count
     */
    public long count(Query query) {
        return mongoTemplate.count(query, getEntityClass());
    }
}
